
package com.hlkj.pay.service.merchant.impl;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import java.net.URLEncoder;
import java.util.List;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.pojo.PageResult;
import com.hlkj.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.hlkj.framework.mybatis.core.query.QueryWrapperX;
import com.hlkj.framework.mybatis.core.utils.MyBatisUtils;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.common.constants.RedisConstants;
import com.hlkj.pay.dto.merchant.MerchantAppChannelListDto;
import com.hlkj.pay.dto.merchant.MerchantAppQueryDto;
import com.hlkj.pay.enums.CommonEnum;
import com.hlkj.pay.enums.MerchantEnum;
import com.hlkj.pay.infrastructure.mapper.merchant.MerchantAppChannelMapper;
import com.hlkj.pay.infrastructure.mapper.merchant.MerchantAppMapper;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAppChannelDO;
import com.hlkj.pay.infrastructure.model.merchant.MerchantAppDO;
import com.hlkj.pay.service.CommonSnFilterService;
import com.hlkj.pay.service.merchant.IMerchantApplicationService;
import com.hlkj.pay.util.AESUtils;
import com.hlkj.pay.util.MerchantUtils;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author HlpayTeam
 * @date 2024/09/28 13:56
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class MerchantApplicationServiceImpl extends CommonSnFilterService implements IMerchantApplicationService {

    private final MerchantAppMapper merchantAppMapper;

    private final MerchantAppChannelMapper merchantAppChannelMapper;

    @Override
    public PageResult<MerchantAppDO> queryPage(MerchantAppQueryDto merchantInfoQueryDO) {
        return merchantAppMapper.selectPage(merchantInfoQueryDO, buildWrapper(merchantInfoQueryDO));
    }

    @Override
    public List<MerchantAppDO> queryList(MerchantAppQueryDto merchantInfoQueryDO) {
        return merchantAppMapper.selectList(buildWrapper(merchantInfoQueryDO));
    }

    @Override
    public MerchantAppDO queryDetail(Long id) {
        return merchantAppMapper.selectById(id);
    }

    @Override
    public void addApp(MerchantAppDO merchantAppDO) {
        merchantAppMapper.insert(merchantAppDO);
    }

    @Override
    public MerchantAppDO queryDefaultApp(String sn) {
        MerchantAppQueryDto merchantAppDO = new MerchantAppQueryDto();
        merchantAppDO.setSn(sn);
        merchantAppDO.setDefaultAppFlag(CommonEnum.YES_NO_TYPE.YES.getCode());
        return queryOne(merchantAppDO);
    }

    @Override
    public MerchantAppDO queryDetailByAppId(String appId) {
        MerchantAppQueryDto merchantAppDO = new MerchantAppQueryDto();
        merchantAppDO.setAppId(appId);
        return queryOne(merchantAppDO);
    }

    @Override
    public MerchantAppDO getByAppId(String appId) {
        return merchantAppMapper.selectOne(MerchantAppDO::getAppId, appId);
    }

    @Override
    public void saveMerchantApp(MerchantAppDO updateMerchantAppDO) {
        merchantAppMapper.insert(updateMerchantAppDO);
    }

    @Override
    @CacheEvict(value = RedisConstants.MERCHANT_APP_INFO, key = "#appId")
    public void updateMerchantApp(MerchantAppDO updateMerchantAppDO, String appId) {
        merchantAppMapper.updateById(updateMerchantAppDO);
    }

    @Override
    @CacheEvict(value = RedisConstants.MERCHANT_APP_INFO, key = "#appId")
    public void updateMerchantAppStatus(String appId, Integer status) {
        MerchantAppDO updateMerchantAppDO = new MerchantAppDO();
        updateMerchantAppDO.setStatus(status);
        MerchantAppQueryDto merchantAppDO = new MerchantAppQueryDto();
        merchantAppDO.setAppId(appId);
        merchantAppMapper.update(updateMerchantAppDO, buildWrapper(merchantAppDO));
    }

    @Override
    @CacheEvict(value = RedisConstants.MERCHANT_APP_INFO, key = "#appId")
    public void delMerchantAppByAppId(String appId, Long id) {
        merchantAppMapper.deleteById(id);
    }

    @CacheEvict(value = RedisConstants.MERCHANT_APP_INFO, key = "#appId")
    public void delMerchantApp(String appId) {
        MerchantAppQueryDto merchantAppDO = new MerchantAppQueryDto();
        merchantAppDO.setAppId(appId);
        merchantAppMapper.delete(buildWrapper(merchantAppDO));
    }

    @Override
    public String generateAppId() {
        return appId(1);
    }

    @Override
    public PageResult<MerchantAppChannelListDto> queryChannelPage(MerchantAppQueryDto merchantAppQueryDto) {
        merchantAppQueryDto.setSortingFields("a.id");
        IPage<MerchantAppChannelListDto> mpPage = MyBatisUtils.buildPage(merchantAppQueryDto);
        QueryWrapper<MerchantAppChannelDO> queryWrapper = buildAppChannelListWrapper(merchantAppQueryDto);
        merchantAppChannelMapper.selectMerchantAppChannelPage(mpPage, queryWrapper);
        return new PageResult(mpPage.getRecords(), mpPage.getTotal(), mpPage.getCurrent(), mpPage.getSize(), mpPage.getPages());
    }

    @Override
    public List<MerchantAppChannelDO> queryChannelInfoList(MerchantAppQueryDto merchantAppQueryDto) {
        LambdaQueryWrapperX<MerchantAppChannelDO> queryWrapperX = buildAppChannelWrapper(merchantAppQueryDto);
        if (merchantAppQueryDto.isPage()) {
            queryWrapperX.last("limit " + merchantAppQueryDto.getPageSize());
        }
        return merchantAppChannelMapper.selectList(queryWrapperX);
    }

    @Override
    public List<MerchantAppChannelDO> queryEffectProvideAppId(MerchantAppQueryDto merchantAppQueryDto) {
        QueryWrapperX<MerchantAppChannelDO> queryWrapperX = buildAppWrapper(merchantAppQueryDto);
        return merchantAppChannelMapper.selectEffectProvideAppId(queryWrapperX);
    }

    @Override
    // @Cacheable(value = RedisConstants.MERCHANT_CHANNEL_APP_LIST, key =
    // "#sn+'_'+#appId+'_'+#providerCode+'_'+#channelCode", unless = "#result ==
    // null || #result.size() == 0")
    public List<MerchantAppChannelDO> queryChannelInfoList(String sn, String appId, String providerCode, String channelCode,Integer status, Integer routerStrategy) {
        MerchantAppQueryDto merchantAppQueryDto = new MerchantAppQueryDto();
        merchantAppQueryDto.setSn(sn);
        merchantAppQueryDto.setAppId(appId);
        merchantAppQueryDto.setProviderCode(providerCode);
        merchantAppQueryDto.setChannelCode(channelCode);
        merchantAppQueryDto.setStatus(status);
        // if (routerStrategy != null && !routerStrategy.equals(CommonEnum.YES_NO_TYPE.NO.getCode())) {
        //     merchantAppQueryDto.setLimitRouter(CommonEnum.YES_NO_TYPE.YES.getCode());
        // }
        merchantAppQueryDto.setSortingFields("id");
        // 顺序排序
        LambdaQueryWrapperX<MerchantAppChannelDO> lambdaQueryWrapperX = buildAppChannelWrapper(merchantAppQueryDto);
        return merchantAppChannelMapper.selectList(lambdaQueryWrapperX);
    }

    @Override
    @Cacheable(value = RedisConstants.MERCHANT_CHANNEL_APP_INFO, key = "#code", unless = "#result == null")
    public MerchantAppChannelDO queryAppChannelDetail(String code) {
        return merchantAppChannelMapper.selectOne(MerchantAppChannelDO::getCode, code);
    }

    @Override
    public MerchantAppChannelDO queryAppChannelDetailBySubMchId(String subMchId) {
        return merchantAppChannelMapper.selectOne(MerchantAppChannelDO::getSubMchId, subMchId);
    }


    @Override
    @Caching(evict = { @CacheEvict(value = RedisConstants.MERCHANT_CHANNEL_APP_LIST, key = "#merchantAppChannelDO.sn+'_'+#merchantAppChannelDO.appId+'*'"), })
    public void saveMerchantAppChannel(MerchantAppChannelDO merchantAppChannelDO) {
        if (merchantAppChannelDO.getAccessType() == null) {
            merchantAppChannelDO.setAccessType(MerchantEnum.DATA_RELATION_TYPE.ACCESS.getCode());
        }
        merchantAppChannelMapper.insert(merchantAppChannelDO);
    }

    @Override
    public void saveBatchMerchantAppChannel(List<MerchantAppChannelDO> merchantAppChannelList) {
        merchantAppChannelList.stream().forEach(merchantAppChannelDO -> {
            merchantAppChannelDO.setAccessType(MerchantEnum.DATA_RELATION_TYPE.ACCESS.getCode());
        });
        merchantAppChannelMapper.insertBatch(merchantAppChannelList);
    }

    @Override
    @Caching(evict = { @CacheEvict(value = RedisConstants.MERCHANT_CHANNEL_APP_INFO, key = "#code"),
            @CacheEvict(value = RedisConstants.MERCHANT_CHANNEL_APP_LIST, key = "#merchantAppChannelDO.sn+'_'+#merchantAppChannelDO.appId+'*'"), })
    public void updateMerchantAppChannel(MerchantAppChannelDO merchantAppChannelDO, String code) {
        merchantAppChannelMapper.updateById(merchantAppChannelDO);
    }

    @Override
    @Caching(evict = { @CacheEvict(value = RedisConstants.MERCHANT_CHANNEL_APP_INFO, key = "#code"),
            @CacheEvict(value = RedisConstants.MERCHANT_CHANNEL_APP_LIST, key = "#sn+'_'+#appId+'*'"), })
    public void delMerchantAppChannel(String code, String sn, String appId) {
        MerchantAppQueryDto queryMerchantAppChannelDO = new MerchantAppQueryDto();
        queryMerchantAppChannelDO.setCode(code);
        merchantAppChannelMapper.delete(buildAppChannelWrapper(queryMerchantAppChannelDO));
    }

    @Override
    @Caching(evict = { @CacheEvict(value = RedisConstants.MERCHANT_CHANNEL_APP_INFO, key = "#code"),
            @CacheEvict(value = RedisConstants.MERCHANT_CHANNEL_APP_LIST, key = "#sn+'_'+#appId+'*'"), })
    public void updateMerchantAppChannelStatus(String code, Integer status, String sn, String appId) {
        MerchantAppChannelDO merchantAppChannelDO = new MerchantAppChannelDO();
        merchantAppChannelDO.setStatus(status);
        MerchantAppQueryDto queryMerchantAppChannelDO = new MerchantAppQueryDto();
        queryMerchantAppChannelDO.setCode(code);
        merchantAppChannelMapper.update(merchantAppChannelDO, buildAppChannelWrapper(queryMerchantAppChannelDO));
    }

    @Override
    public void updateMerchantAppChanneLimitRouter(String code, Integer limitRouter) {
        MerchantAppChannelDO merchantAppChannelDO = new MerchantAppChannelDO();
        merchantAppChannelDO.setLimitRouter(limitRouter);
        MerchantAppQueryDto queryMerchantAppChannelDO = new MerchantAppQueryDto();
        queryMerchantAppChannelDO.setCode(code);
        merchantAppChannelMapper.update(merchantAppChannelDO, buildAppChannelWrapper(queryMerchantAppChannelDO));
    }

    @Override
    public void delMerchantAppBySn(String sn) {
        MerchantAppQueryDto merchantAppQueryDto = new MerchantAppQueryDto();
        merchantAppQueryDto.setSn(sn);
        List<MerchantAppDO> merchantAppList = merchantAppMapper.selectList(buildWrapper(merchantAppQueryDto));
        if (CollectionUtils.isEmpty(merchantAppList)) {
            return;
        }
        merchantAppList.stream().forEach(merchantAppDO -> {
            delMerchantApp(merchantAppDO.getAppId());
        });
    }

    @Override
    @Caching(evict = { @CacheEvict(value = RedisConstants.MERCHANT_CHANNEL_APP_INFO, key = "#merchantAppChannel.code") })
    public void updateAppConfig(MerchantAppChannelDO merchantAppChannel, String channelParams) {
        MerchantAppChannelDO update = new MerchantAppChannelDO();
        update.setId(merchantAppChannel.getId());
        update.setAppConfigJson(channelParams);
        merchantAppChannelMapper.updateById(update);
    }

    @Override
    public MerchantAppChannelDO queryDetailByAccessId(Long accessId, MerchantEnum.DATA_RELATION_TYPE accessType) {
        MerchantAppQueryDto merchantAppQueryDto = new MerchantAppQueryDto();
        merchantAppQueryDto.setAccessId(accessId);
        merchantAppQueryDto.setAccessType(accessType.getCode());
        LambdaQueryWrapper<MerchantAppChannelDO> queryWrapper = buildAppChannelWrapper(merchantAppQueryDto);
        queryWrapper.orderByAsc(MerchantAppChannelDO::getId);
        queryWrapper.last("limit 1");
        return merchantAppChannelMapper.selectOne(queryWrapper);
    }

    @Override
    public MerchantAppChannelDO queryDetailByAccessId(String appId, Long accessId) {
        MerchantAppQueryDto merchantAppQueryDto = new MerchantAppQueryDto();
        merchantAppQueryDto.setAccessId(accessId);
        merchantAppQueryDto.setAppId(appId);
        LambdaQueryWrapper<MerchantAppChannelDO> queryWrapper = buildAppChannelWrapper(merchantAppQueryDto);
        queryWrapper.orderByAsc(MerchantAppChannelDO::getId);
        queryWrapper.last("limit 1");
        return merchantAppChannelMapper.selectOne(queryWrapper);
    }

    @Override
    public CommonResult<String> cashierUrl(Long id) {
        try {
            MerchantAppDO merchantAppDO = merchantAppMapper.selectById(id);
            if (merchantAppDO == null) {
                return CommonResult.error(MerchantResultCode.MERCHANT_APP_NOT_EXIST);
            }
            String encryptCode = AESUtils.encrypt(merchantAppDO.getAppId());
            return CommonResult.success(URLEncoder.encode(encryptCode));
        }
        catch (Exception e) {
            e.printStackTrace();
            return CommonResult.error(MerchantResultCode.CASHIER_PAYMENT_URL_EXCEPTION);
        }
    }

    String appId(int num) {
        if (num > 100) {
            return "";
        }
        String appId = MerchantUtils.code("APP_", 10);
        MerchantAppQueryDto merchantAppQueryDto = new MerchantAppQueryDto();
        merchantAppQueryDto.setAppId(appId);
        MerchantAppDO merchantAppDO = queryOne(merchantAppQueryDto);
        if (merchantAppDO != null) {
            return appId(num + 1);
        }
        return appId;
    }

    MerchantAppDO queryOne(MerchantAppQueryDto merchantAppDO) {
        LambdaQueryWrapper<MerchantAppDO> queryWrapper = buildWrapper(merchantAppDO);
        queryWrapper.last("limit 1");
        return merchantAppMapper.selectOne(queryWrapper);
    }

    /**
     * 查询条件封装
     *
     * @param merchantAppQueryDto
     * @return
     */
    LambdaQueryWrapper<MerchantAppDO> buildWrapper(MerchantAppQueryDto merchantAppQueryDto) {
        filterSn(merchantAppQueryDto);
        LambdaQueryWrapperX<MerchantAppDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eqIfPresent(MerchantAppDO::getAppId, merchantAppQueryDto.getAppId());
        queryWrapper.inIfPresent(MerchantAppDO::getAppId, merchantAppQueryDto.getAppIds());
        queryWrapper.eqIfPresent(MerchantAppDO::getSysType, merchantAppQueryDto.getSysType());
        queryWrapper.likeIfPresent(MerchantAppDO::getAppId, merchantAppQueryDto.getLikeAppId());
        queryWrapper.likeIfPresent(MerchantAppDO::getSn, merchantAppQueryDto.getLikeSn());
        queryWrapper.eqIfPresent(MerchantAppDO::getSn, merchantAppQueryDto.getSn());
        queryWrapper.inIfPresent(MerchantAppDO::getSn, merchantAppQueryDto.getSnList());
        queryWrapper.likeIfPresent(MerchantAppDO::getAppName, merchantAppQueryDto.getLikeAppName());
        queryWrapper.eqIfPresent(MerchantAppDO::getAppName, merchantAppQueryDto.getAppName());
        queryWrapper.eqIfPresent(MerchantAppDO::getDefaultAppFlag, merchantAppQueryDto.getDefaultAppFlag());

        return queryWrapper;
    }

    /**
     * 查询条件封装
     *
     * @param merchantAppQueryDto
     * @return
     */
    QueryWrapper<MerchantAppChannelDO> buildAppChannelListWrapper(MerchantAppQueryDto merchantAppQueryDto) {
        filterSn(merchantAppQueryDto);
        QueryWrapperX<MerchantAppChannelDO> queryWrapper = new QueryWrapperX<>();
        queryWrapper.eq("a.delete_flag", CommonEnum.YES_NO_TYPE.NO.getCode());
        queryWrapper.likeIfPresent("app.app_name", merchantAppQueryDto.getLikeAppName());
        queryWrapper.eqIfPresent("a.code", merchantAppQueryDto.getCode());
        queryWrapper.eqIfPresent("a.sub_mch_id", merchantAppQueryDto.getSubMchId());
        queryWrapper.eqIfPresent("a.provider_code", merchantAppQueryDto.getProviderCode());
        queryWrapper.eqIfPresent("a.channel_code", merchantAppQueryDto.getChannelCode());
        queryWrapper.eqIfPresent("a.app_id", merchantAppQueryDto.getAppId());
        queryWrapper.eqIfPresent("a.sn", merchantAppQueryDto.getSn());
        queryWrapper.eqIfPresent("a.status", merchantAppQueryDto.getStatus());
        queryWrapper.eqIfPresent("a.sn", merchantAppQueryDto.getSnList());
        queryWrapper.geIfPresent("a.create_time", merchantAppQueryDto.getStartQueryTime());
        queryWrapper.leIfPresent("a.create_time", merchantAppQueryDto.getEndQueryTime());
        return queryWrapper;
    }

    /**
     * 查询条件封装
     *
     * @param merchantAppQueryDto
     * @return
     */
    LambdaQueryWrapperX<MerchantAppChannelDO> buildAppChannelWrapper(MerchantAppQueryDto merchantAppQueryDto) {
        filterSn(merchantAppQueryDto);
        LambdaQueryWrapperX<MerchantAppChannelDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eqIfPresent(MerchantAppChannelDO::getAppId, merchantAppQueryDto.getAppId());
        queryWrapper.eqIfPresent(MerchantAppChannelDO::getSn, merchantAppQueryDto.getSn());
        queryWrapper.inIfPresent(MerchantAppChannelDO::getSn, merchantAppQueryDto.getSnList());
        queryWrapper.eqIfPresent(MerchantAppChannelDO::getProviderCode, merchantAppQueryDto.getProviderCode());
        queryWrapper.eqIfPresent(MerchantAppChannelDO::getChannelCode, merchantAppQueryDto.getChannelCode());
        queryWrapper.eqIfPresent(MerchantAppChannelDO::getAccessId, merchantAppQueryDto.getAccessId());
        queryWrapper.eqIfPresent(MerchantAppChannelDO::getCode, merchantAppQueryDto.getCode());
        queryWrapper.eqIfPresent(MerchantAppChannelDO::getStatus, merchantAppQueryDto.getStatus());
        queryWrapper.eqIfPresent(MerchantAppChannelDO::getSubMchId, merchantAppQueryDto.getSubMchId());
        queryWrapper.eqIfPresent(MerchantAppChannelDO::getAccessType, merchantAppQueryDto.getAccessType());
        queryWrapper.eqIfPresent(MerchantAppChannelDO::getLimitRouter, merchantAppQueryDto.getLimitRouter());
        queryWrapper.eqIfPresent(MerchantAppChannelDO::getType, merchantAppQueryDto.getType());

        queryWrapper.gtIfPresent(MerchantAppChannelDO::getId, merchantAppQueryDto.getMinId());
        queryWrapper.ltIfPresent(MerchantAppChannelDO::getId, merchantAppQueryDto.getMaxId());

        return queryWrapper;
    }

    /**
     * 查询条件封装
     *
     * @param merchantAppQueryDto
     * @return
     */
    QueryWrapperX<MerchantAppChannelDO> buildAppWrapper(MerchantAppQueryDto merchantAppQueryDto) {
        filterSn(merchantAppQueryDto);
        QueryWrapperX<MerchantAppChannelDO> queryWrapper = new QueryWrapperX<>();
        queryWrapper.eqIfPresent("a.delete_flag", CommonEnum.YES_NO_TYPE.NO.getCode());
        queryWrapper.eqIfPresent("a.app_id", merchantAppQueryDto.getAppId());
        queryWrapper.eqIfPresent("a.provider_code", merchantAppQueryDto.getProviderCode());
        queryWrapper.eqIfPresent("a.channel_code", merchantAppQueryDto.getChannelCode());
        queryWrapper.eqIfPresent("a.status", merchantAppQueryDto.getStatus());
        if (merchantAppQueryDto.getCommonAppType() != null) {
            switch (merchantAppQueryDto.getCommonAppType()) {
                case WECHAT_PUB:
                    queryWrapper.isNotNull("b.wechat_pub_id");
                    break;
                case WECHAT_MIN:
                    queryWrapper.isNotNull("b.wechat_mini_id");
                    break;
                case ALIPAY_PUB:
                    queryWrapper.isNotNull("b.alipay_pub_id");
                    break;
                case ALIPAY_MIN:
                    queryWrapper.isNotNull("b.alipay_mini_id");
                    break;
            }
        }
        return queryWrapper;
    }
}
